# class to represent changes to a particular issue.  these changes are always
# attached to a comment, since comments/updates happen at the same time.  there
# are two types of changes that exist as subclasses:
# * IssueAttrChange - changes to an attribute (like percent_complete)
# * IssueRefChange - changes to a association reference (like status)
class IssueChange < ActiveRecord::Base
  belongs_to :comment, :counter_cache => true

  # shortcut to create records (both kinds) from a hash
  # :type must be specified as either :attr or :ref
  # :from and :to represent the content of the change
  # :attr must be specified for :type => :attr
  def self.record( options = {} )
    case options[:type]
    when :ref
      IssueRefChange.new( :changed_to => options[:to],
                          :changed_from => options[:from] )
    when :attr
      raise( ArgumentError, 'attr must be specified' ) unless options[:attr]
      attr_changed = options[:attr]
      from = options[:from].to_s if options[:from]
      to = options[:to].to_s if options[:to]
      IssueAttrChange.new( :from_str => from, :to_str => to, :attr => attr_changed )
    else
      raise ArguemntError, ':type must be specified (either :attr or :ref)'
    end
  end

  # from an array of IssueChanges, returns the first IssueRefChange with the
  # given type
  def self.select_ref_change( changes, type )
    changes.each do |c|
      return c if c.class == IssueRefChange and c.changed_to_type == type
    end
    return nil
  end

  # from an array of IssueChanges, returns the first IssueAttrChange with the
  # given attr
  def self.select_attr_change( changes, attr_str )
    changes.each do |c|
      return c if c.class == IssueAttrChange and c.attr == attr_str
    end
    return nil
  end

  def inspect
    'IssueChange'
  end
end

# concrete class to represent attribute changes
class IssueAttrChange < IssueChange
  def display?
    true
  end

  def from
    from_str
  end

  def from=( s )
    from_str = s
  end

  def to
    to_str
  end

  def to=( s )
    to_str = s
  end

  def inspect
    "IssueAttrChange( #{self.send :attr}: '#{from.inspect}' => '#{to.inspect}' )"
  end
end

# concrete class to represent reference changes
class IssueRefChange < IssueChange
  belongs_to :changed_from, :polymorphic => true
  belongs_to :changed_to, :polymorphic => true

  def display?
    changed_to.name and changed_to.name.any?
  end

  def from
    changed_from
  end

  def from=( o )
    changed_from = o
  end

  def to
    changed_to
  end

  def to=( o )
    changed_to = o
  end

  def inspect
    "IssueRefChange( #{changed_from_type}: '#{from.inspect}' => '#{to.inspect}' )"
  end
end
